为什么80%的码农都做不了架构师?>>>   

http://nealma.com/2016/04/30/spring-boot-4-security/

### 前言
    shiro可以完全解决有关安全的问题,但是项目大多使用的是Spring框架,所以没必要在引入其他依赖,
    so,还是折腾下亲儿子的东东吧

开发环境:
OS: Mac 10.11.6
IDE: IDEA
Build: Maven

以前都是使用xml来配置,但是spring-boot提倡Java Config的使用,
    折腾下来还是很多收获的。有关权限管理,行内大都秉承
    以角色为基础的访问控制(Role-based access control, RBAC)
    在Spring Security中,实现原理很简单,通过AOP对所要管理的资源(url或者method)进行拦截,
    在其内部维护了一条安全过滤链,有用户服务(UserDetailsService)、身份认证服务(AuthenticationProvider)、
    访问决策管理(AccessDecisionManager)、记住我(remember-me)等普世功能。
    当然,使用Spring-boot离不开Java Config,come on,just do it.

### WebSecurityConfig

```java

@Configuration
@ComponentScan("com.caogen")
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
@PropertySource("classpath:application.properties")
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

protected final Logger LOGGER = LoggerFactory.getLogger(getClass());

@Autowired
    private MyAuthenticationProvider authenticationProvider;//自定义验证

@Autowired
    private MyUserDetailsService userDetailsService;

@Autowired
    private MyPersistentTokenRepository tokenRepository;

@Autowired
    private MyAccessDecisionManager accessDecisionManager;

@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authenticationProvider);
        auth.userDetailsService(userDetailsService);
    }

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .headers()
                    .frameOptions().sameOrigin().disable()//disable X-Frame-Options
                .authorizeRequests()
//                    .accessDecisionManager(accessDecisionManager)//用注解替换,如果不使用注解,取消注释
                    .anyRequest().fullyAuthenticated()//其他url需要鉴权
                .and()
                    .formLogin()
                        .usernameParameter("username")
                        .passwordParameter("password")
                        .loginProcessingUrl("/login")
                        .loginPage("/login")
                        .failureUrl("/login?error")
                        .permitAll()
                .and()
                    .logout()
                        .deleteCookies("JSESSIONID")
                        .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                        .logoutSuccessUrl("/login")
                .and()
                    .rememberMe()
                        .tokenRepository(tokenRepository)
                        .rememberMeServices(rememberMeServices())
                        .rememberMeParameter("remember-me").key("key")
                        .tokenValiditySeconds(86400)
                .and()
                    .csrf().disable() //disable csrf
                    .sessionManagement().maximumSessions(1);
    }

@Bean
    public RememberMeServices rememberMeServices() {
        // Key must be equal to rememberMe().key()
        PersistentTokenBasedRememberMeServices rememberMeServices =
                new PersistentTokenBasedRememberMeServices("key", userDetailsService, tokenRepository);
        rememberMeServices.setCookieName("remember-me");
        rememberMeServices.setParameter("remember-me");
        rememberMeServices.setTokenValiditySeconds(864000);
        return rememberMeServices;
    }

}

```

### MyUserDetailsService

```java
/**
 * SPRING SECURITY用户登录处理
 */
@Service("MyUserDetailsServiceImpl")
public class MyUserDetailsService implements UserDetailsService {

protected final Logger LOGGER = LoggerFactory.getLogger(getClass());

@Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private RoleMapper roleMapper;
    @Autowired
    private UserRoleLinkMapper userRoleLinkMapper;

@Autowired
    private ResourceService resourceService;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

LOGGER.info("loadUserByUsername --> [{}]", username);

SysUser sysUser = new SysUser();
        sysUser.setUsername(username);
        List<SysUser> userList = sysUserMapper.select(sysUser);
        if (userList == null || userList.size() == 0) {
            throw new UsernameNotFoundException("username not found.");
        }
        sysUser = userList.get(0);
        UserRoleLink userRoleLink = new UserRoleLink();
        userRoleLink.setUserId(sysUser.getId());

List<UserRoleLink> userRoleLinks = userRoleLinkMapper.select(userRoleLink);
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();

List<Long> roleIds = new ArrayList<>();

Optional<List<UserRoleLink>> userRoleLinksOptional = Optional.ofNullable(userRoleLinks);
        userRoleLinksOptional.ifPresent(userRoleLinks1 -> {
            userRoleLinks1.forEach(userRoleLink1 -> {
                roleIds.add(userRoleLink1.getRoleId());
                if(userRoleLink1.getRoleId() == 1L){
                    GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_root");//root角色特权
                    grantedAuthorities.add(grantedAuthority);
                }
            });
        });

//        List<Role> roles = roleMapper.selectBatch(roleIds);//V1.0
        List<Resource> resources = resourceService.selectByRoleId(roleIds.toArray(new Long[0]));

if(resources != null && resources.size() > 0){
            resources.forEach(resource -> {
                GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_"+resource.getLink());//必须ROLE_为前缀
                grantedAuthorities.add(grantedAuthority);
            });
        }

LOGGER.info("grantedAuthorities --> {}", grantedAuthorities);
        return new User(username, sysUser.getPassword(), true, true, true, true, grantedAuthorities);
    }

}
```

### MyAuthenticationProvider

```java

/**
 * 自定义验证方式
 */
@Component
public class MyAuthenticationProvider implements AuthenticationProvider{

protected final Logger LOGGER = LoggerFactory.getLogger(getClass());

@Autowired
    private MyUserDetailsService userService;

@Override
    public Authentication authenticate(Authentication authentication)
                                                throws AuthenticationException {
        String username = authentication.getName();
        String password = (String) authentication.getCredentials();

UserDetails user = userService.loadUserByUsername(username);

LOGGER.info("password={}, needPassword={}", password, user.getPassword());
        //密码匹配验证
        if (passwordEncoder().matches(password, user.getPassword())) {
            Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
            return new UsernamePasswordAuthenticationToken(user, password, authorities);
        }

throw new BadCredentialsException("Wrong password.");
    }

@Bean
   public PasswordEncoder passwordEncoder(){
       return new BCryptPasswordEncoder();
   }

@Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}

```

### MyAccessDecisionManager

```java
/**
 * 验证资源跟角色之间的关系
 */
@Component
public class MyAccessDecisionManager implements AccessDecisionManager {

protected final Logger LOGGER = LoggerFactory.getLogger(getClass());

@Autowired
    private RoleService roleService;

/**
     * // authentication 为用户所被赋予的权限, configAttributes 为访问相应的资源应该具有的权限。
     * @param authentication
     * @param object
     * @param configAttributes
     * @throws AccessDeniedException
     * @throws InsufficientAuthenticationException
     */
    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
            throws AccessDeniedException, InsufficientAuthenticationException {

Collection<GrantedAuthority> userHasRoles =
                (Collection<GrantedAuthority>) authentication.getAuthorities();

LOGGER.info("CurrentUser={} CurrentHasRoles = {}", authentication.getName(), Arrays.asList(userHasRoles));

//放行[超级管理员]角色
        Iterator<GrantedAuthority> iterator = userHasRoles.iterator();
        while (iterator.hasNext()){
            GrantedAuthority grantedAuthority = iterator.next();
            if("系统管理员".equals(grantedAuthority.getAuthority())){
                return;
            }
        }
        LOGGER.info("1 CurrentUser={} CurrentHasRoles = {}", authentication.getName(), Arrays.asList(userHasRoles));

Collection<GrantedAuthority> uriHasRoles = getGrantedAuthoritys(object);
        if (uriHasRoles == null || uriHasRoles.size() == 0) {
            return;
        }

Optional<Collection<GrantedAuthority>> grantedAuthoritiesForOptional =
                Optional.ofNullable(userHasRoles);

try{
            grantedAuthoritiesForOptional.ifPresent(userHasRolesNotNull -> {
                userHasRolesNotNull.forEach(userHasRole -> {
                    uriHasRoles.forEach(uriHasRole -> {
                        LOGGER.info("userHasRole={}, uriHasRole={}", userHasRole, uriHasRole);
                        if (userHasRole.getAuthority().equals(uriHasRole.getAuthority())) {
                            throw new AppException("break");
                        }
                    });
                });
            });
        }catch(AppException be){
            return;
        }

throw new AccessDeniedException("Access Denied.");
    }

private Collection<GrantedAuthority> getGrantedAuthoritys(Object object) {
        FilterInvocation filterInvocation = (FilterInvocation) object;
        String uri = new StringBuilder(filterInvocation.getRequestUrl()).deleteCharAt(0).toString();
        if("".equals(uri)){
            return null;
        }
        List<Role> uriHasRoles = roleService.selectByResourceURI(uri);
        LOGGER.info("fullRequestUrl={}, requestUrl={}, uriHasRoles={}",
                filterInvocation.getFullRequestUrl(),
                filterInvocation.getRequestUrl(),
                uriHasRoles);

if (uriHasRoles == null || uriHasRoles.size() == 0) {
            return null;
        }
        Collection<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        uriHasRoles.forEach(item -> {
            GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(item.getName());
            grantedAuthorities.add(grantedAuthority);
        });
        return grantedAuthorities;
    }

@Override
    public boolean supports(ConfigAttribute configAttribute) {
        return true;
    }

@Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}

```

### Remember-Me 使用database方式,MyPersistentTokenRepository

```java

/**
 * 记住我 持久化方式
 */
@Component
public class MyPersistentTokenRepository implements PersistentTokenRepository {

protected final Logger LOGGER = LoggerFactory.getLogger(getClass());

@Autowired
    private RememberMeMapper remembermeMapper;

@Override
    public void createNewToken(PersistentRememberMeToken persistentRememberMeToken) {
        RememberMe rememberMe = new RememberMe();
        rememberMe.setUsername(persistentRememberMeToken.getUsername());
        rememberMe.setSeries(persistentRememberMeToken.getSeries());
        rememberMe.setDate(persistentRememberMeToken.getDate());
        rememberMe.setTokenValue(persistentRememberMeToken.getTokenValue());
        remembermeMapper.insert(rememberMe);
    }

@Override
    public void updateToken(String s, String s1, Date date) {
        RememberMe rememberMe = new RememberMe();
        rememberMe.setUsername("");
        rememberMe.setSeries(s);
        rememberMe.setTokenValue(s1);
        rememberMe.setDate(date);
        remembermeMapper.updateByPK(rememberMe);
    }

@Override
    public PersistentRememberMeToken getTokenForSeries(String s) {
        RememberMe rememberMe = remembermeMapper.selectByPK(s);
        PersistentRememberMeToken persistentRememberMeToken =
                new PersistentRememberMeToken(rememberMe.getUsername(),
                        rememberMe.getSeries(),
                        rememberMe.getTokenValue(),
                        rememberMe.getDate()
                );
        return persistentRememberMeToken;
    }

@Override
    public void removeUserTokens(String s) {
        remembermeMapper.deleteByPK(s);
    }
}

```

### 如果用注解的方式,及@RolesAllowed(jsr250)

那么务必将jsr250Enabled=true在config中声明,然后再所需控制的方法上加上注解,
   jsr250有三个注解,分别是@RolesAllowed,@PermitAll,@DenyAll,功能跟名字一样,
   一目了然,如果有多个角色,可以@RolesAllowed({"ROLE_A", "ROLE_B"}),这里角色的前缀
   一定是"ROLE_",约定好的,就不要计较了。

### 结束

使用过程中,总是下意识的按照Spring Security提供的思路去实现,无可厚非,但是我仔细琢磨后,
   觉得从便利性和实用方面考虑,每个方法上的注解要约定好名称,例如:

```

@RolesAllowed({"ROLE_roles:view"})
   public void find{...}

@RolesAllowed({"ROLE_roles:update"})
   public void update{...}

@RolesAllowed({"ROLE_roles:delete"})
   public void delete{...}

@RolesAllowed({"ROLE_roles:create"})
   public void create{...}

````

这样,表面是把权限赋予了角色,还能Dynamic Resource,何乐而不为呢。

转载于:https://my.oschina.net/nealma/blog/852586

Spring-Boot (四) 集成Spring Security相关推荐

  1. 【Spring Boot组件集成实战】集成Kaptcha谷歌验证码

    更多精彩内容,请访问 Spring Boot组件集成实战专栏 ! 推荐项目:一套基于Spring Boot+Layui的内容管理系统/快速开发脚手架(含完整的开发文档.演示网址等) 文章目录 1. 验 ...

  2. Spring Boot + Shiro 集成

    2019独角兽企业重金招聘Python工程师标准>>> Spring Boot + Shiro 集成 Shiro 是一个流行的 Java 安全框架. 其实 Spring 有一个 Sp ...

  3. spring boot(四):thymeleaf使用详解

    spring boot(四):thymeleaf使用详解 在上篇文章springboot(二):web综合开发中简单介绍了一下thymeleaf,这篇文章将更加全面详细的介绍thymeleaf的使用. ...

  4. Spring Boot 工程集成全局唯一ID生成器 Vesta

    2019独角兽企业重金招聘Python工程师标准>>> 本文内容脑图如下: 文章共 760字,阅读大约需要 2分钟 ! 概 述 在前一篇文章 <Spring Boot工程集成全 ...

  5. Spring Boot日志集成

    Spring Boot日志框架 Spring Boot支持Java Util Logging,Log4j2,Lockback作为日志框架,如果你使用starters启动器,Spring Boot将使用 ...

  6. 在spring boot中集成Swagger

    Swagger 在spring boot中集成Swagger 新建一个swagger项目 maven依赖 <!-- https://mvnrepository.com/artifact/io.s ...

  7. Spring Boot 快速集成第三方登录功能

    Spring Boot 快速集成第三方登录功能 前言 此 demo 主要演示 Spring Boot 项目如何使用 史上最全的第三方登录工具 - JustAuth 实现第三方登录,包括 QQ 登录.G ...

  8. Spring/Spring boot正确集成Quartz及解决@Autowired失效问题

    Spring/Spring boot正确集成Quartz及解决@Autowired失效问题 参考文章: (1)Spring/Spring boot正确集成Quartz及解决@Autowired失效问题 ...

  9. Spring Boot实践 | 利用Spring Security快速搞定权限控制

    目录 开始之前 快速开始 使用内存签名服务 使用数据库签名服务 使用自定义签名服务 限制请求 强制使用HTTPS 防止跨站点伪造请求 用户认证功能 在java web工程中,一般使用Servlet过滤 ...

  10. Spring Boot 项目使用Spring Security防护CSRF攻击实战

    Spring Boot与Spring Security Spring Boot项目结合Spring Security ,可以实现用户认证和用户授权. Spring Security的用户认证可以是配置 ...

最新文章

  1. ggplot2笔记5:通过图层构建图像
  2. tensorflow量化感知训练_tensorflow
  3. 三级结构_kegg pathway三级层级结构转对应表格
  4. oracle 加载数据戽_走进大数据丨 ETL - Load(数据加载)
  5. 只能在栈上或者堆上创建对象
  6. 【Swin Transformer Block】的整体流程如下:
  7. android 购票代码,android完美电影购票源码
  8. python爬虫框架scrapy操作步骤
  9. java实验报告13答案_(完整版)Java程序设计实验报告
  10. 语音识别技术:2018年主要参与者的完整指南
  11. 你有被银行套路过吗?| 一文教你计算真实的年化利率
  12. 二本计算机软件工程专业大学排名,哪些二本大学的软件工程专业最好
  13. css竖向箭头符号_「右箭头符号」html实现右箭头 - seo实验室
  14. Linux系统下操作的常用快捷键
  15. 将HEX文件通过Keil软件进行下载
  16. 个人用户永久免费,可自动升级版Excel插件,使用VSTO开发,Excel催化剂功能第12波-快速生成、读取、导出条形码二维码...
  17. 设计模式 - 模版模式
  18. 【LeetCode】1905-统计子岛屿
  19. python第七十九天--第十四周作业
  20. 平狄克微观经济学笔记和课后习题答案

热门文章

  1. 表示探索、探究的几个词
  2. Firefox下强制页面缓存失效的设置方法
  3. NCRE四级网络工程师考题详解----三级索引结构
  4. 蓝桥杯第五届省赛JAVA真题----n级台阶
  5. php session作用,PHP中Session的作用
  6. css 实现一个尖角_一个讲述了 CSS 相关的技巧、动画实现 的开源项目(60篇相关文章)...
  7. 关闭linux系统中读写页缓存,Linux文件系统FAQ
  8. concat合并的数组会有顺序么_javascript concat 数组与数组或数组与字符串的合并
  9. nginx动静分离配置_Nginx动静分离
  10. java中日历类:Calendar