开发者社区> benjaminwhx> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

spring_security安全框架详解

简介:
+关注继续查看

本文是在项目中用到spring Security3来进行登录验证时才进行学习的,写的比较片面,请大家提出问题。

优秀的安全框架连接:

http://blog.csdn.net/z69183787/article/details/38542109

http://blog.csdn.net/z69183787/article/details/38542125

http://blog.csdn.net/z69183787/article/details/38542139

首先要在web.xml中配置

[html] view plain copy
 print?
  1. <context-param>  
  2. <param-name>contextConfigLocation</param-name>  
  3. <param-value>  
  4. classpath*:/applicationContext.xml  
  5. classpath*:/applicationContext-security.xml  
  6. <!--classpath*:/applicationContext-timer.xml  
  7. --></param-value>  
  8. </context-param>  

加载security配置文件

加入过滤器:

[html] view plain copy
 print?
  1. <filter>  
  2.         <filter-name>springSecurityFilterChain</filter-name>  
  3.         <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  
  4.     </filter>  
[html] view plain copy
 print?
  1. <filter-mapping>  
  2. <span style="white-space:pre">        </span><filter-name>springSecurityFilterChain</filter-name>  
  3. <span style="white-space:pre">        </span><url-pattern>/*</url-pattern>  
  4. <span style="white-space:pre">        </span><dispatcher>REQUEST</dispatcher>  
  5. <span style="white-space:pre">        </span><dispatcher>FORWARD</dispatcher>  
  6. <span style="white-space:pre">    </span></filter-mapping>  

session监听器:

[html] view plain copy
 print?
  1. <listener>   
  2.     <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>   
  3.    </listener>   


用户是怎样认证的?

在我们的安全系统中,当一个用户在我们的登录form中提供凭证后,这些凭证信息必须与凭证存储中的数据进行校验以确定下一步的行为,凭证的校验涉及到一系列的逻辑组件,它们封装了认证过程。

站在一个较高的层次上看,你可以看到有三个主要的组件负责这项重要的事情:

接口名                                         描述/角色

AbstractAuthenticationProcessingFilter 它在基于web的认证请求中使用。处理包含认证信息的请求,如认     证信息可能是form POST提交的, SSO信息或者其他用户提供的。创 建一个部分完整的 Authentication对象以在链中 传递凭证信息。

 

AuthenticationManager 它用来校验用户的凭证信息,或 者会抛出一个特定的异常(校验 失败的情况)或者完整填充 Authentication象,将会包含 了权限信息。

 

AuthenticationProvider 它为AuthenticationManager 提供凭证校验。一些 AuthenticationProvider的实 现基于凭证信息的存储,如数 库,来判定凭证信息是否可以被 认可。


spring_security_login是什么?我们怎么达到这个页面的?

URLspring_security_login部分表明这是一个默认的登录页面并且是在DefaultLoginPageGeneratingFilter中命名的,我们可以使用配置属性来修改这个页面的名字从而使它对我们应用来说是唯一的。

建议修改登录页URL的默认值,修改后不仅能够对应用或搜索引擎更友好,而且能够隐藏你使用Spring Security做为安全实现的事实。

下面就是一段登录页面login.jsp的代码:

[html] view plain copy
 print?
  1. <!-- j_spring_security_check  
  2.         并不对应任何应用中的物理资源。它只是UsernamePasswordAuthenticationFilter监视的一个基于form登录的URL   
  3.           
  4.         文本域的名字是UsernamePasswordAuthenticationFilter规定的,并借鉴了Java EE Servlet 2.x的规范  
  5.         规范要求登录的form使用特定的名字并且form的action要为特定的j_security_check  
  6.         值。这样的实际模式目标是允许基于Java EE servlet-based的应用能够与servlet容器的安全设施以标准的方式  
  7.         连接起来。  
  8. -->  
  9. <form id="loginForm" action="${ctx}/j_spring_security_check" method="post">  
  10.                 <div style="height:40px;line-height:40px"> <input class="input_users" id="j_username" name="j_username" type="text" /></div>  
  11.                 <div style="height:49px;line-height:49px"> <input class="input_users" id="j_password" name="j_password" type="password" /></div>  
  12.                 <div style="height:60px;line-height:60px"> <input type="button" name="login"  class="input_login" onfocus="this.blur()" onclick="check();"/></div>  
  13.             </form>  

这是一个利用spring安全框架登录的页面,点击提交后,会检查用户名和密码是否为所需正则,再进行提交,提交会根据配置文件进行认证匹配

 

下面给出application-security.xml

[html] view plain copy
 print?
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:s="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  5.                         http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd"  
  6.     default-lazy-init="true">  
  7.   
  8.     <description>SpringSecurity安全配置</description>  
  9.       
  10.     <!-- http安全配置 -->  
  11.     <!-- auto-config设为true会自动提供以下3个认证相关的功能  
  12.         1、HTTP基本认证  
  13.         2、Form登录认证  
  14.         3、退出  
  15.         你也可以使用配置元素来实现这三个功能,比使用auto-config提供的功能更精确  
  16.      -->  
  17.     <s:http auto-config="true" use-expressions="true" >  
  18.         <s:intercept-url pattern="/css/**" filters="none" />  
  19.         <s:intercept-url pattern="/images/**" filters="none" />  
  20.         <s:intercept-url pattern="/js/**" filters="none" />  
  21.         <s:form-login login-page="/login.action"  
  22.             default-target-url="/main.html" authentication-failure-url="/login.action?error=true" />  
  23.         <!-- logout-success-url退出指向的路径     
  24.              invalidate-session是否让session失效  
  25.              logout-url退出的路径,默认不写为j_spring_security_logout  
  26.              success-handler-ref 对一个LogoutSuccessHandler实现的引用。  
  27.         -->  
  28.         <s:logout logout-success-url="/login.action" invalidate-session="true"/>  
  29.         <!--   
  30.             Session固化是恶意用户试图窃取系统中一个未认证用户的session。对攻击者来说,可以通过各种技术来  
  31.             获取用户session的唯一标识(例如,JSESSIONID)。如果攻击者创建了带有用户JSESSIONID的cookie或者  
  32.             URL参数,他就能够访问用户的session。  
  33.             session-fixation-protection有三个值:  
  34.             none:使得session固化攻击失效,不会配置SessionManagementFilter(除非其它的  
  35.                 <session-management>属性不是默认值)  
  36.             migrateSession(默认):当用户经过认证后分配一个新的session,它保证原session的所有属性移到  
  37.                 新session中。我们将在后面的章节中讲解,通过基于bean的方式如何进行这样的配置。  
  38.             newSession:当用户认证后,建立一个新的session,原(未认证时)session的属性不会进行移到新session中来。  
  39.          -->  
  40.            
  41.          <!--  
  42.             invalid-session-url:是指session过期后重定向到的url地址 
  43.           -->  
  44.         <s:session-management>  
  45.             <!-- 你不想使用session并发控制,你可以可以这样做。只需将max-sessions的值设置为-1,这样  
  46.                 session跟踪会保持可用,但没有最大session个数的限制。  
  47.                 expired-url:用户再次登录重定向的地址  
  48.              -->  
  49.             <s:concurrency-control max-sessions="1" error-if-maximum-exceeded="false" expired-url="/message.jsp"/>  
  50.         </s:session-management>  
  51.     </s:http>  
  52.     <!-- 认证配置, 使用userDetailsService提供的用户信息 -->  
  53.     <s:authentication-manager alias="authenticationManager">  
  54.         <s:authentication-provider user-service-ref="userDetailsService">  
  55.             <!-- 对密码进行加密 -->  
  56.             <s:password-encoder hash="md5" />  
  57.             <s:password-encoder hash="plaintext">  
  58.             </s:password-encoder>  
  59.         </s:authentication-provider>  
  60.     </s:authentication-manager>  
  61.   
  62.     <!-- 项目实现的用户查询服务 -->  
  63.     <bean id="userDetailsService" class="com.wiseweb.pom.service.account.UserDetailsServiceImpl" />  
  64. </beans>  


 

认证配置中的authentication-manager是对用户输入的信息进行验证的操作,我们通过实现UserDetailService进行实现。

下面是UserDetailServiceImpl代码图


[html] view plain copy
 print?
  1.  package com.wiseweb.pom.service.account;  
  2.    
  3.  import com.google.common.collect.Lists;  
  4. import com.google.common.collect.Sets;  
  5. import com.wiseweb.pom.entity.Role;  
  6. import com.wiseweb.pom.entity.Menu;  
  7. import com.wiseweb.util.ElintUtil;  
  8. import com.wiseweb.util.LoginUser;  
  9.   
  10. import java.util.List;  
  11. import java.util.Set;  
  12.   
  13. import org.springframework.beans.factory.annotation.Autowired;  
  14. import org.springframework.dao.DataAccessException;  
  15. import org.springframework.security.core.GrantedAuthority;  
  16. import org.springframework.security.core.authority.GrantedAuthorityImpl;  
  17. import org.springframework.security.core.userdetails.UserDetails;  
  18. import org.springframework.security.core.userdetails.UserDetailsService;  
  19. import org.springframework.security.core.userdetails.UsernameNotFoundException;  
  20. import org.springframework.transaction.annotation.Transactional;  
  21.    
  22.  @Transactional(readOnly=false)  
  23.  public class UserDetailsServiceImpl  
  24.    implements UserDetailsService {  
  25.    private AccountManager accountManager;  
  26.    
  27.    public UserDetails loadUserByUsername(String username)  
  28.      throws UsernameNotFoundException, DataAccessException{  
  29.      com.wiseweb.pom.entity.User user = this.accountManager.findUniqueUser(username);  
  30.      if (user == null) {  
  31.          throw new UsernameNotFoundException("用户" + username + " 不存在");  
  32.      }  
  33.      @SuppressWarnings("rawtypes")  
  34.     Set grantedAuths = obtainGrantedAuthorities(user);  
  35.      boolean enabled = true;//是不是激活的  
  36.      boolean accountNonExpired = true;//账户是否过期  
  37.      boolean credentialsNonExpired = true;//认证是否过期  
  38.      boolean accountNonLocked = true;//是否锁定  
  39.    
  40.      @SuppressWarnings("unchecked")  
  41.     UserDetails userdetails = new org.springframework.security.core.userdetails.User(  
  42.        user.getLoginName(), user.getPassWord(), enabled,   
  43.        accountNonExpired, credentialsNonExpired, accountNonLocked,   
  44.        grantedAuths);  
  45.    
  46.      return new LoginUser(userdetails, user);  
  47.    }  
  48.    
  49.    @SuppressWarnings("unchecked")  
  50. private Set<GrantedAuthority> obtainGrantedAuthorities(com.wiseweb.pom.entity.User user)  
  51.    {  
  52.      @SuppressWarnings("rawtypes")  
  53.     Set authSet = Sets.newHashSet();  
  54.        
  55.      List<Role>  roles = user.getRoles();  
  56.      Set<Menu> menus = Sets.newHashSet();  
  57.      for(Role role :roles){  
  58.          System.out.println(role.getRoleName());  
  59.          menus.addAll(role.getMenus());  
  60.      }  
  61.      List<Menu> menusList = Lists.newArrayList() ;  
  62.      menusList.addAll(menus);  
  63.      user.setMenus(menusList);  
  64.      for (Menu menu : user.getMenus()) {  
  65.        if (menu.getNodeType().intValue() == 3) {  
  66.          authSet.add(new GrantedAuthorityImpl(menu.getPrefixedName()));  
  67.        }  
  68.      }  
  69.      user.setMenus(new ElintUtil().menuRelationSet(user.getMenus()));  
  70.      return authSet;  
  71.    }  
  72.    
  73.    @Autowired  
  74.    public void setAccountManager(AccountManager accountManager) {  
  75.      this.accountManager = accountManager;  
  76.    }  
  77.  }  

这个类通过实现UserDetailService来达到返回一个org.springframework.security.core.userdetails.UserDetails的目的。

下面是我的User实体类:

[html] view plain copy
 print?
  1.  package com.wiseweb.pom.entity;  
  2.    
  3.  import com.google.common.collect.Lists;  
  4. import com.wiseweb.pom.entity.IdEntity;  
  5. import com.wiseweb.pom.entity.Menu;  
  6. import com.wiseweb.pom.entity.Department;  
  7.   
  8. import java.util.ArrayList;  
  9. import java.util.Date;  
  10. import java.util.List;  
  11.   
  12.  import javax.persistence.Column;  
  13. import javax.persistence.Entity;  
  14. import javax.persistence.JoinColumn;  
  15. import javax.persistence.JoinTable;  
  16. import javax.persistence.ManyToMany;  
  17. import javax.persistence.ManyToOne;  
  18. import javax.persistence.Table;  
  19. import javax.persistence.Transient;  
  20.   
  21. import org.hibernate.annotations.Cache;  
  22. import org.hibernate.annotations.CacheConcurrencyStrategy;  
  23. import org.hibernate.annotations.Fetch;  
  24. import org.hibernate.annotations.FetchMode;  
  25.    
  26.  @SuppressWarnings("unused")  
  27. @Entity  
  28.  @Table(name="wise_user")  
  29.  public class User extends IdEntity  
  30.  {  
  31.    private static final long serialVersionUID = 1L;  
  32.    private String loginName;  
  33.    private String name;  
  34.    private String passWord;  
  35.    private String userStatus;  
  36.    private String email;  
  37.    private String telephone;  
  38.    private Integer sex;  
  39.    private Date lastLoginTime ;  
  40.    private String userSign ;  
  41.    private Integer orderType ;  
  42.    private List<Role> roles = Lists.newArrayList() ;  
  43.    private Integer flag ;  
  44.     private List<Menu> menus = Lists.newArrayList();  
  45.     private List<HomeMenu> homeMenus = new ArrayList<HomeMenu>() ;  
  46.    private Department department = null;  
  47.    
  48.    public User()  
  49.    {  
  50.    }  
  51.    
  52.    public User(String loginName, String name, String passWord, String userStatus, String email, String telephone)  
  53.    {  
  54.      this.loginName = loginName;  
  55.      this.name = name;  
  56.      this.passWord = passWord;  
  57.      this.userStatus = userStatus;  
  58.      this.email = email;  
  59.      this.telephone = telephone;  
  60.    }  
  61.    public User(String loginName, String name, String passWord, String userStatus, String email, String telephone,Integer sex)  
  62.    {  
  63.      this.loginName = loginName;  
  64.      this.name = name;  
  65.      this.passWord = passWord;  
  66.      this.userStatus = userStatus;  
  67.      this.email = email;  
  68.      this.telephone = telephone;  
  69.      this.sex = sex;  
  70.    }  
  71.    @Column(name="login_name"nullable=falselength=45)  
  72.    public String getLoginName() {  
  73.      return this.loginName;  
  74.    }  
  75.    
  76.    public void setLoginName(String loginName) {  
  77.      this.loginName = loginName;  
  78.    }  
  79.    
  80.    @Column(name="name"nullable=falselength=45)  
  81.    public String getName() {  
  82.      return this.name;  
  83.    }  
  84.    
  85.    public void setName(String name) {  
  86.      this.name = name;  
  87.    }  
  88.    
  89.    @Column(name="pass_word"nullable=falselength=45)  
  90.    public String getPassWord() {  
  91.      return this.passWord;  
  92.    }  
  93.    
  94.    public void setPassWord(String passWord) {  
  95.      this.passWord = passWord;  
  96.    }  
  97.    
  98.    @Column(name="user_status"nullable=falselength=45)  
  99.    public String getUserStatus() {  
  100.        if(this.userStatus != null){  
  101.            return this.userStatus;  
  102.        }else{  
  103.            return "1";  
  104.        }  
  105.    }  
  106.    
  107.    public void setUserStatus(String userStatus) {  
  108.      this.userStatus = userStatus;  
  109.    }  
  110.    
  111.    @Column(name="email"length=45)  
  112.    public String getEmail() {  
  113.      return this.email;  
  114.    }  
  115.    
  116.    public void setEmail(String email) {  
  117.      this.email = email;  
  118.    }  
  119.    
  120.    @Column(name="telephone"length=45)  
  121.    public String getTelephone() {  
  122.      return this.telephone;  
  123.    }  
  124.    
  125.    public void setTelephone(String telephone) {  
  126.      this.telephone = telephone;  
  127.    }  
  128.      
  129.    @Column(name="sex")  
  130.     public Integer getSex() {  
  131.        if(this.sex != null){  
  132.            return sex;  
  133.        }else{  
  134.            return 1;  
  135.        }  
  136.     }  
  137.       
  138.     public void setSex(Integer sex) {  
  139.         this.sex = sex;  
  140.     }  
  141.    public void setMenus(List<Menu> menus) {  
  142.      this.menus = menus;  
  143.    }  
  144.    
  145.    @Transient  
  146.    public List<Menu> getMenus() {  
  147.      return this.menus;  
  148.    }  
  149.      
  150.    @Transient  
  151.    public List<HomeMenu> getHomeMenus() {  
  152.     return this.homeMenus;  
  153.    }  
  154.   
  155.    public void setHomeMenus(List<HomeMenu> homeMenus) {  
  156.     this.homeMenus = homeMenus;  
  157.    }  
  158.   
  159. @Column(name="last_login_time"nullable=falselength=45)  
  160.    public Date getLastLoginTime() {  
  161.     return lastLoginTime;  
  162.     }  
  163.       
  164.     public void setLastLoginTime(Date lastLoginTime) {  
  165.         this.lastLoginTime = lastLoginTime;  
  166.     }  
  167.     @Column(name="user_sign"length=45)  
  168.     public String getUserSign() {  
  169.         return userSign;  
  170.     }  
  171.       
  172.     public void setUserSign(String userSign) {  
  173.         this.userSign = userSign;  
  174.     }  
  175.     @Column(name="order_type")  
  176.     public Integer getOrderType() {  
  177.         return orderType;  
  178.     }  
  179.     public void setOrderType(Integer orderType) {  
  180.         this.orderType = orderType;  
  181.     }  
  182.     @Column(name="flag"nullable=false)  
  183.     public Integer getFlag() {  
  184.         return flag;  
  185.     }  
  186.       
  187.     public void setFlag(Integer flag) {  
  188.         this.flag = flag;  
  189.     }  
  190.     @ManyToOne  
  191.     @JoinColumn(name="depart_id")  
  192.     public Department getDepartment() {  
  193.         return department;  
  194.     }  
  195.   
  196.     public void setDepartment(Department department) {  
  197.         this.department = department;  
  198.     }  
  199. //  @ManyToMany  
  200. //     @JoinTable(name="wise_role_user"joinColumns={@javax.persistence.JoinColumn(name="user_id")}, inverseJoinColumns={@javax.persistence.JoinColumn(name="role_id")})  
  201. //     @Fetch(FetchMode.SUBSELECT)  
  202. //     @Cache(usage=CacheConcurrencyStrategy.READ_WRITE)  
  203.          
  204.     @ManyToMany  
  205.     @JoinTable(name = "wise_role_user"joinColumns = { @javax.persistence.JoinColumn(name = "user_id") }, inverseJoinColumns = { @javax.persistence.JoinColumn(name = "role_id")})  
  206.     @Fetch(FetchMode.SUBSELECT)  
  207.     @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)  
  208.     public List<Role> getRoles() {  
  209.         return roles;  
  210.     }  
  211.   
  212.     public void setRoles(List<Role> roles) {  
  213.         this.roles = roles;  
  214.     }  
  215.       
  216.  }  

有人可能要问了?为什么userDetails返回的是一个new LoginUser(userdetails, user)呢?

我们来看看LoginUser代码

[html] view plain copy
 print?
  1.  package com.wiseweb.util;  
  2.    
  3.  import com.wiseweb.pom.entity.HomeMenu;  
  4. import com.wiseweb.pom.entity.Role;  
  5. import com.wiseweb.pom.entity.User;  
  6. import com.wiseweb.pom.entity.Menu;  
  7. import com.wiseweb.pom.entity.Department;  
  8.   
  9.  import java.util.Collection;  
  10. import java.util.Date;  
  11. import java.util.List;  
  12.   
  13. import org.springframework.security.core.GrantedAuthority;  
  14. import org.springframework.security.core.userdetails.UserDetails;  
  15.    
  16.  public class LoginUser implements UserDetails{  
  17.        
  18.    private static final long serialVersionUID = 1366695319372075545L;  
  19.    private UserDetails userDetails;  
  20.    private User user;  
  21.    
  22.    public LoginUser(UserDetails userDetails, User user)  
  23.    {  
  24.      this.userDetails = userDetails;  
  25.      this.user = user;  
  26.    }  
  27.    
  28.    public Collection<GrantedAuthority> getAuthorities()  
  29.    {  
  30.      return this.userDetails.getAuthorities();  
  31.    }  
  32.    
  33.    public String getPassword()  
  34.    {  
  35.      return this.userDetails.getPassword();  
  36.    }  
  37.    
  38.    public String getUsername()  
  39.    {  
  40.      return this.userDetails.getUsername();  
  41.    }  
  42.    
  43.    public boolean isAccountNonExpired()  
  44.    {  
  45.      return this.userDetails.isAccountNonExpired();  
  46.    }  
  47.    
  48.    public boolean isAccountNonLocked()  
  49.    {  
  50.      return this.userDetails.isAccountNonLocked();  
  51.    }  
  52.    
  53.    public boolean isCredentialsNonExpired()  
  54.    {  
  55.      return this.userDetails.isCredentialsNonExpired();  
  56.    }  
  57.    
  58.    public boolean isEnabled()  
  59.    {  
  60.      return this.userDetails.isAccountNonExpired();  
  61.    }  
  62.    
  63.    public UserDetails getUserDetails() {  
  64.      return this.userDetails;  
  65.    }  
  66.    
  67.    public String getLoginName() {  
  68.      return this.user.getLoginName();  
  69.    }  
  70.    
  71.    public String getName() {  
  72.      return this.user.getName();  
  73.    }  
  74.    //by lib  
  75.    public Long getUserId() {  
  76.          return this.user.getId();  
  77.    }  
  78.    
  79.    public String getPassWord() {  
  80.      return this.user.getPassWord();  
  81.    }  
  82.    
  83.    public String getUserStatus() {  
  84.      return this.user.getUserStatus();  
  85.    }  
  86.    
  87.    public String getEmail() {  
  88.      return this.user.getEmail();  
  89.    }  
  90.    
  91.    public String getTelephone() {  
  92.      return this.user.getTelephone();  
  93.    }  
  94.    
  95.    public List<Menu> getMenus() {  
  96.      return this.user.getMenus();  
  97.    }  
  98.    
  99.    public Long getId() {  
  100.      return this.user.getId();  
  101.    }  
  102.    public Integer getSex(){  
  103.        return this.user.getSex() ;  
  104.    }  
  105.    public Date lastLoginName(){  
  106.        return this.user.getLastLoginTime() ;  
  107.    }  
  108.    public String userSign(){  
  109.        return this.user.getUserSign() ;  
  110.    }  
  111.    public Integer getOrderType(){  
  112.        return this.user.getOrderType() ;  
  113.    }  
  114.    public List<Role> getRoles(){  
  115.        return this.user.getRoles() ;  
  116.    }  
  117.    public Integer getFlag(){  
  118.        return this.user.getFlag() ;  
  119.    }  
  120.    public Department getDepartment(){  
  121.        return this.user.getDepartment() ;  
  122.    }  
  123.    public List<HomeMenu> getHomeMenus(){  
  124.        return this.user.getHomeMenus() ;  
  125.    }  
  126.    public User getUser() {  
  127.         return user;  
  128.     }  
  129.   
  130. @Override  
  131. public int hashCode() {  
  132.     return this.userDetails.getUsername().hashCode() ;  
  133. }  
  134.   
  135. @Override  
  136. public boolean equals(Object obj) {  
  137.     if(obj instanceof LoginUser){  
  138.         return this.userDetails.getUsername().equals(((LoginUser)obj).getUsername());  
  139.     }  
  140.     return false ;  
  141. }  
  142. }  

这样如果用户的用户名和密码都正确后,spring security就会把信息放入userDetails中,并且创建一个名叫"SPRING_SECURITY_CONTEXT"的session,我们可以通过下面的方式拿到Login对象。

[html] view plain copy
 print?
  1. LoginUser user = (LoginUser)((SecurityContext)  
  2.            ServletActionContext.getRequest().getSession().getAttribute(  
  3.            "SPRING_SECURITY_CONTEXT")).getAuthentication()  
  4.            .getPrincipal();  

这样我们就可以在代码中拿到user对象进行一系列操作了。


下面我要讲一下项目中实现单点登录(一方登录,另一方再用同一帐号登录时,第一个用户被顶)的实现

我做单点登录的时候,网上很多教程很片面的说了一下spring security的配置,例如下面的配置:

[html] view plain copy
 print?
  1. <s:session-management>  
  2.             <s:concurrency-control max-sessions="1" error-if-maximum-exceeded="false" expired-url="/message.jsp"/>  
  3.         </s:session-management>  

可以这个配置好了之后,还是不起作用的,最根本的要去重写hashCode和equals方法,为什么呢?

因为第一个用户登录了之后,是把用户信息(帐号密码)放在userDetails中的,第二个用户再进行登录后,他的信息又会放入userDetails中,这时候就要在放入的时候进行比较,如果相同则告诉spring容器有相同的帐号登录了,就请求spring踢除第一个用户。

所以我们必须比较userDetails中的username,那么在哪里比较呢,我这个例子是在LoginUser中比较的,当然大家实现的不一样,原理其实都一样。

下面是最核心的一段代码:

[html] view plain copy
 print?
  1. @Override  
  2. public int hashCode() {  
  3.     return this.userDetails.getUsername().hashCode() ;  
  4. }  
  5.   
  6. @Override  
  7. public boolean equals(Object obj) {  
  8.     if(obj instanceof LoginUser){  
  9.         return this.userDetails.getUsername().equals(((LoginUser)obj).getUsername());  
  10.     }  
  11.     return false ;  
  12. }  

接着又来了一个问题,我要做成像腾讯qq那样,重复登录后及时被顶并且提示“您的账户已在异地登录”之类的提示语,告诉用户

下面是我一开始的配置

[html] view plain copy
 print?
  1. <s:session-management invalid-session-url="login.jsp">  
  2.             <s:concurrency-control max-sessions="1" error-if-maximum-exceeded="false" expired-url="/message.jsp"/>  
  3.         </s:session-management>  

这是message.jsp代码

[html] view plain copy
 print?
  1. <%@ page contentType="text/html;charset=UTF-8" %>  
  2. <%@ include file="/common/taglibs.jsp" %>  
  3. <%@ page import="org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter" %>  
  4. <%@ page import="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter" %>  
  5. <%@ page import="org.springframework.security.web.WebAttributes" %>  
  6. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
  7. <html xmlns="http://www.w3.org/1999/xhtml">  
  8. <head>  
  9. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  
  10. <!--框架必需start-->  
  11. <script type="text/javascript" src="${ctx }/js/jquery-1.4.js"></script>  
  12. <script type="text/javascript" src="${ctx }/js/framework.js"></script>  
  13. <script type="text/javascript" src="${ctx }/js/attention/messager.js"></script>  
  14. <link href="${ctx }/css/import_basic.css" rel="stylesheet" type="text/css"/>  
  15. <link  rel="stylesheet" type="text/css" id="skin" prePath="${ctx }/"//>  
  16. <!--框架必需end-->  
  17. <!-- 系统必须 start-->  
  18. <script type="text/javascript" src="${ctx}/js/table.js"></script>  
  19. <script type="text/javascript" src="${ctx}/js/table/treeTable.js"></script>  
  20. <script type="text/javascript" src="${ctx}/js/elint.js"></script>  
  21. <!-- 系统必须 end-->  
  22. <!--截取文字start-->  
  23. <script type="text/javascript" src="${ctx }/js/text/text-overflow.js"></script>  
  24. <script type='text/javascript'>  
  25.             $(function(){  
  26.                 top.Dialog.alert('您的帐号已在其他地方登录,您当前的帐号已退出!',function (){  
  27.                     location='<%=path%>/login.jsp' ;  
  28.                 });  
  29.             });  
  30.         </script>  
  31. </head>  
  32. <body>  
  33. </body>  
  34. </html>  


可是每次被顶后,用户都直接跳到login.jsp去了,而没有走message.jsp,我就很苦恼。

于是我看了一下SessionmanagerFilter和ConcurrentSessionFilter的源码,认为我的配置是正确的。

下面是两个源码,也可以去我的博客去看全文:http://blog.csdn.net/benjamin_whx/article/details/39204699

SessionManagerFilter:

[html] view plain copy
 print?
  1. public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)    
  2.         throws IOException, ServletException {    
  3.     HttpServletRequest request = (HttpServletRequest) req;    
  4.     HttpServletResponse response = (HttpServletResponse) res;    
  5.     //省略……    
  6.      //判断当前session中是否有SPRING_SECURITY_CONTEXT属性    
  7.     if (!securityContextRepository.containsContext(request)) {    
  8.         Authentication authentication = SecurityContextHolder.getContext().getAuthentication();    
  9.     
  10.         if (authentication != null && !authenticationTrustResolver.isAnonymous(authentication)) {    
  11.             try {    
  12.                 //再通过sessionStrategy执行session固化、并发处理    
  13.                    //与UsernamePasswordAuthenticationFilter时处理一样,后面会仔细分析。    
  14.                 sessionStrategy.onAuthentication(authentication, request, response);    
  15.             } catch (SessionAuthenticationException e) {    
  16.                 SecurityContextHolder.clearContext();    
  17.                 failureHandler.onAuthenticationFailure(request, response, e);    
  18.                 return;    
  19.             }    
  20.             //把SecurityContext设置到当前session中    
  21.             securityContextRepository.saveContext(SecurityContextHolder.getContext(), request, response);    
  22.         } else {    
  23.             if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) {    
  24.                 if (invalidSessionUrl != null) {    
  25.                     request.getSession();    
  26.                     redirectStrategy.sendRedirect(request, response, invalidSessionUrl);    
  27.     
  28.                     return;    
  29.                 }    
  30.             }    
  31.         }    
  32.     }    
  33.     
  34.     chain.doFilter(request, response);    
  35. }    

ConcurrentSessionFilter

[html] view plain copy
 print?
  1. public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)    
  2.         throws IOException, ServletException {    
  3.     HttpServletRequest request = (HttpServletRequest) req;    
  4.     HttpServletResponse response = (HttpServletResponse) res;    
  5.     
  6.     HttpSession session = request.getSession(false);    
  7.     if (session != null) {    
  8.         //这个SessionInformation是在执行SessionManagementFilter时通过sessionRegistry构造的并且放置在map集合中的    
  9.         SessionInformation info = sessionRegistry.getSessionInformation(session.getId());    
  10.         //如果当前session已经注册了    
  11.         if (info != null) {    
  12.             //如果当前session失效了    
  13.             if (info.isExpired()) {    
  14.                 // Expired - abort processing    
  15.                 //强制退出    
  16.                 doLogout(request, response);    
  17.                 //目标url为expired-url标签配置的属性值    
  18.                 String targetUrl = determineExpiredUrl(request, info);    
  19.                 //跳转到指定url    
  20.                 if (targetUrl != null) {    
  21.                     redirectStrategy.sendRedirect(request, response, targetUrl);    
  22.     
  23.                     return;    
  24.                 } else {    
  25.                     response.getWriter().print("This session has been expired (possibly due to multiple concurrent " +    
  26.                             "logins being attempted as the same user).");    
  27.                     response.flushBuffer();    
  28.                 }    
  29.     
  30.                 return;    
  31.             } else {    
  32.                 // Non-expired - update last request date/time    
  33.                 //session未失效,刷新时间    
  34.                 info.refreshLastRequest();    
  35.             }    
  36.         }    
  37.     }    
  38.     
  39.     chain.doFilter(request, response);    
  40. }    

这一句话的意思就是没有指定expired-url就会默认写出这么一段话
[html] view plain copy
 print?
  1. response.getWriter().print("This session has been expired (possibly due to multiple concurrent " +    
  2.                             "logins being attempted as the same user).");   

可是我还是没有走message.jsp,带着疑问我把session-manager中的invalid-session-url="login.jsp"去掉了,这样就可以了,是不是spring先判断走SessionManagerFilter?如果session为空就直接根据invalid-session-url跳走了呢?希望有人给我指出。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Spring Security(一)
Spring Security(一)
48 0
Spring Security(五)
Spring Security(五)
48 0
Spring Security(三)
Spring Security(三)
60 0
Spring Boot Security 详解
Spring Security,这是一种基于 Spring AOP 和 Servlet 过滤器的安全框架。它提供全面的安全性解决方案,同时在 Web 请求级和方法调用级处理身份确认和授权。
1915 0
Spring安全权限管理(Spring Security)
1.Spring Security简要介绍 Spring Security以前叫做acegi,是后来才成为Spring的一个子项目,也是目前最为流行的一个安全权限管理框架,它与Spring紧密结合在一起。
777 0
Spring Security 入门详解(转)
1.Spring Security介绍 Spring Security是基于spring的应用程序提供声明式安全保护的安全性框架,它提供了完整的安全性解决方案,能够在web请求级别和方法调用级别 处理身份证验证和授权.它充分使用了依赖注入和面向切面的技术.   Spring securi...
1998 0
+关注
194
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载