javascript
Spring-Boot (四) 集成Spring Security
為什么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的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 老男孩IT教育在线3期新学员司毅第一期作
- 下一篇: 2017最新nginx+keepaliv