Spring security 自定义登录与权限控制

一、先说必要的配置文件:

1、web.xml文件添加上



   springSecurityFilterChain
   
      org.springframework.web.filter.DelegatingFilterProxy
   


   springSecurityFilterChain
   /*


2、application-context.xml配置文件

在application-context配置文件中加入spring security配置文件的引用:

xml version="1.0" encoding="UTF-8"?>
>
   >
       ......此处省略
   
   id="viewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
     ......  
     
     name="exposeSpringMacroHelpers" value="true"/>
     name="contentType" value="text/html;charset=UTF-8">
  
      name="toolboxConfigLocation">
      /WEB-INF/toolbox.xml
      
    
resource ="/securityConfig.xml" />

3、securityConfig.xml文件:

xml version="1.0" encoding="UTF-8"?>
xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xmlns:security="http://www.springframework.org/schema/security"
   xsi:schemaLocation="http://www.springframework.org/schema/beans 
             http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/security 
         http://www.springframework.org/schema/security/spring-security-3.2.xsd">

   
   <security:http auto-config="true" use-expressions="true" access-denied-page="/denied">
      
      
      
< security :intercept-url pattern ="/login" access ="permitAll" />
      <security:intercept-url pattern="/logout" access="permitAll" />
      <security:intercept-url pattern="/slr/prd/**" access="hasAnyRole('SHOW_PRD','ADD_PRD','MOD_PRD','DEL_PRD')" />
      <security:intercept-url pattern="/slr/order/**" access="hasAnyRole('SHOW_ORD','CONFIRM_ORD','CANCEL_ORD')" />
      <security:intercept-url pattern="/slr/mgm/**" access="hasAnyRole('SHOW_ACCT','ADD_ACCT','MOD_ACCT','DEL_ACCT')" />
       
      <security:form-login login-page="/login" default-target-url="/welcome"/>
      <security:session-management session-authentication-strategy-ref="concurrentSessionControlStrategy" /> 
   security:http>
 
  
   
   id="concurrentSessionControlStrategy" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
       name="sessionRegistry" ref="sessionRegistry" />
       name="maximumSessions" value="1">
   

   id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />

   
   <security:authentication-manager alias="authenticationManager">
      
      <security:authentication-provider user-service-ref="userDetailsService">
      <security:password-encoder ref="passwordEncoder"/>
      security:authentication-provider>
   security:authentication-manager>

   
   class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" id="passwordEncoder"/>

   
   id="userDetailsService" class="com.security.UserDetailsServiceImpl" />


4、toolbox.xml文件:


   type="string">
      version
      1.3
   
   ......
   
      sec
      request
      com.security.SecurityVelocity
   

这样在vm界面中就可以直接使用啦~

#if($sec.hasAnyRole("SHOW_PRD","ADD_PRD","MOD_PRD","DEL_PRD"))
          >商品管理
          
#end

二、配置文件中的hasAnyRole()方法和UserDetailService的实现

1.SecurityVelocity.java:

public class SecurityVelocity {
   
   public boolean hasAnyRole(String ...roles) {
      return isRole(roles);
   }
   
    /***
     * 前端传入数组参数
     * @param viewRole 可变数组 1个或者多个
     * @return 是否有权限
     */
    private boolean isRole(String ...roles){
       /** 前端数组为空 */
       if(null == roles || roles.length <= 0){
          return false;
       }
       /** 获取当前用户登录对象 */
       UserDetails userDetails = null;
       try {
          userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
      } catch (Exception e) {
         return false;
      }
       if(null == userDetails){
          return false;
       }
       /** 获取当前用户登录对象所有权限 */
      Collectionextends GrantedAuthority> authorities = userDetails.getAuthorities();
      if(null == authorities){
         return false;
      }
      boolean flag = false;
      Iteratorextends GrantedAuthority> iter = authorities.iterator();
      while (iter.hasNext()) {
         GrantedAuthority grantedAuthority = iter.next();
         /**遍历前端列表的所有角色*/
         for (String vr : roles) {
            if(vr.equals(grantedAuthority.getAuthority())){
               flag = true;
               break;
             }
         }
         if (flag) break; // 已经匹配上角色了则不再需要匹配其它角色。
      }
      return flag;
    }
}

2.UserDetailServiceImpl.java

public class UserDetailsServiceImpl implements UserDetailsService {
   
   @Autowired
   private UserAuthService userAuthService;
   
   @Autowired
   private UserService userService;
   
   public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
      
      User securityUser = null;
      
      UserVo mngVo = userService.getSellerByMobile(userName);
      //权限设置
        Collection grantedAuthorities = this.getGrantedAuthorities(mngVo);
        
        boolean enables = true;  
        boolean accountNonExpired = true;  
        boolean credentialsNonExpired = true;  
        boolean accountNonLocked = true; 
        securityUser = new User(mngVo.getId().toString(), mngVo.getPassword(),
              enables, accountNonExpired, credentialsNonExpired, accountNonLocked, grantedAuthorities);  
        return securityUser;
        
   }
   
   
   /**
     * 根据用户获取该用户拥有的权限
     * @param user 当前用户
     * @return 用户角色集合
     */
    private Set getGrantedAuthorities(SellerVo user) {
        Set grantedAuthorities = new HashSet();
        if(null != user){
            List auths = userAuthService.getUserRoleListByUserId(user.getId());
             if(null != auths && auths.size() > 0 ) {
                 for(AuthorityVo auth : auths) {
                     grantedAuthorities.add(new SimpleGrantedAuthority(auth.getCode()+""));
                 }  
             }
        }
        return grantedAuthorities;  
    }
   

}

这个类主要是在登录时调用来设置用户权限信息的。


三:自己登录逻辑与springsecurity结合

public class LoginController extends BaseController {
    ......
    // show login page
    @RequestMapping("login")
    public String index(Model model, HttpServletRequest request, HttpServletResponse response) {        
       // 判断登录状态是否还有效,有效的话自动登录
       final UserVo  Vo = getLoginUser(request);	
       if (userVo != null && userVo.getId() != null) {	
	  //登录信息有效需要将权限信息放入SecurityContextHolder的上下文信息中心,否则可能会造成循环重定向,服务器报错。
	  Authentication authentication = (Authentication) request.getAttribute(CommonConstants.USER_AUTH);
          SecurityContextHolder.getContext().setAuthentication(authentication);
          // redirect to homepage
          return "redirect:/";
       }
	//返回登录页
        return LOGIN;
    }

    // do login action
    @RequestMapping("do_login")
    public String doLogin(Model model, HttpServletResponse response, HttpServletRequest request, 
                        String mobile, String password) {
      log.info("User input data: mobile = " + mobile);      
       if (StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)) {
          model.addAttribute("msg", "请输入手机号和密码!");
            return INDEX;
       }
       ......

      try {
         UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(mobile, password);
	 //调用loadUserByUsername设置权限信息
         Authentication authentication = authenticationManager.authenticate(authRequest); 
         SecurityContextHolder.getContext().setAuthentication(authentication);
          log.info("恭喜用户  " + mobile() + " 登录成功。");
         ......	
 
  
 
  
	 response.sendRedirect('上一次访问的页面或welcome');
} catch (IOException ex) {
log .warn( "Error happened" ) ;
}
return INDEX ;

四、登录页login.vm

html>
lang="en">

    charset="UTF-8">
    </span><span style="font-family:'宋体';">登录页</span><span style="color:rgb(232,191,106);">
...... >

登录

> >
id= "loginForm" name= "loginForm" action= " /do_login" METHOD= "post" > > 商家登录

id= "y_lg_02" > $!{ msg }

> type= "text" placeholder= " 手机号 " value= " $!{ mobile } " />
> type= "password" placeholder= " " value= " $!{ password } " />
> type= "checkbox" /> 自动登录

type= "submit" value= " 立即登录 " />

五、注意点:

用spring security默认的登录,登录页的form的action="/manage/j_spring_security_check"这样写,默认会调用UsernamePasswordAuthenticationFilter中的验证信息。

自己写的登录逻辑主要是要把验证信息写入到spring security的上下文中,三行主要的代码如下:

UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(mobile, password);
//调用loadUserByUsername设置权限信息         
Authentication authentication = authenticationManager.authenticate(authRequest); 
         SecurityContextHolder.getContext().setAuthentication(authentication);

对于自动登录的,需要把权限也放入spring security上下文中,但是生成 UsernamePasswordAuthenticationToken对象需要密码的明文,这里我是把它放在缓存中,是自动登录的直接从缓存中取出来放进spring security上下文中。

final UserVo  Vo = getLoginUser(request);
       if (userVo != null && userVo.getId() != null) {
//登录信息有效需要将权限信息放入SecurityContextHolder的上下文信息中心,否则可能会造成循环重定向,服务器报错。
          Authentication authentication = (Authentication) request.getAttribute(CommonConstants.USER_AUTH);
          SecurityContextHolder.getContext().setAuthentication(authentication);
          // redirect to homepage
          return "redirect:/";
       }

自动登录开始把权限也放入spring security上下文中,结果出现了循环重定向,是因为之前选择了自动登录,则if条件为true,这时重定向的/login,又开始执行登录页面的代码,造成了重定向死循环。


看到别人写的一篇好文,有助于理解:http://blog.csdn.net/zy_cookie/article/details/49535413

你可能感兴趣的:(Spring security 自定义登录与权限控制)