理解不透彻,还在摸索中,写下来自己看
一、创建maven-web项目
web.xml配置
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <context-param> <param-name>webAppRootKey</param-name> <param-value>spring.root</param-value> </context-param> <!-- Spring的log4j监听器 --> <listener> <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class> </listener> <!-- Spring view分发器 mvc Servlet的名字是(<servlet-name>)是dispatcher,所以Spring的约定胜于配置(Convention over Configuration,CoC)将会在WEB-INF目录下寻找名为dispatcher-servelt.xml的配置文件。我们没有覆 盖这种默认行为,你能在WEB-INF目录下找到这个文件,它包含了一些Spring MVC相关的配置。也可以指定路径 --> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!--在web应用的部署描述文件中,用来配置ContextLoaderListener的XML文件地址是在<context-param>元素中给出的 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-core.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 字符集 过滤器 --> <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- Spring security --> <filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- security管理会话监听 --> <listener id="Listener_Spring_Security_Session_Event_Publisher"> <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class> </listener> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
mvc.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd "> <context:component-scan base-package="spring.controller" /> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/" /> <property name="suffix" value=".jsp" /> </bean> </beans>security.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:security="http://www.springframework.org/schema/security" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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.0.xsd"> <!-- use-expressions:Spring 表达式语言配置访问控制 --> <security:http auto-config="true" use-expressions="false"> <security:intercept-url pattern="/home/*" access="ROLE_USER" /> <!--默认登录页面 default-target-url默认跳转的页面 always-use-default-target登录成功后默认进入home.jsp --> <security:form-login login-page="/portal/login" default-target-url="/home/index.jsp" always-use-default-target="true" login-processing-url="/login"/> <!-- 退出配置 logout-success-url:退出成功后跳转的页面 , 如果session配置了invalid-session-url默认跳转到指定的页面,logout-success-url不起作用 --> <security:logout logout-url="/logout" logout-success-url="/portal/login" /> <!-- 过滤器 --> <security:custom-filter ref="pwdAuthentication" after="SECURITY_CONTEXT_FILTER"/> <!--管理会话 invalid-session-url:session失效后跳转到的页面 invalid-session-url="/sessionTimeout.jsp"--> <security:session-management > <security:concurrency-control max-sessions="1"/> </security:session-management> </security:http> <security:authentication-manager alias="authenticationManager"> <!-- <security:authentication-provider> <security:user-service> <security:user name="admin" password="123456" authorities="ROLE_USER" /> </security:user-service> </security:authentication-provider> --> <!-- 注意这里的ref属性定义。如果没有使用CAS认证,此处一般定义user-service-ref属性。这两个属性的区别在于 ref:直接将ref依赖的bean注入到AuthenticationProvider的providers集合中 user-service-ref:定义DaoAuthenticationProvider的bean注入到AuthenticationProvider的providers集合中, 并且DaoAuthenticationProvider的变量userDetailsService由user-service-ref依赖的bean注入。 --> <security:authentication-provider ref="userService"></security:authentication-provider> </security:authentication-manager> <bean id="pwdAuthentication" class="spring.security.PwdAuthenticationProcessingFilter"> <property name="authenticationManager" ref="authenticationManager"></property> <property name="faildPage" value="/portal/login"></property> </bean> </beans>
springSecurity的登录验证是由org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter这个过滤器来完成的,在该类的父类AbstractAuthenticationProcessingFilter中有一个AuthenticationManager接口属性,验证工作主要是通过这个AuthenticationManager接口的实例来完成的。在默认情况下,springSecurity框架会把org.springframework.security.authentication.ProviderManager类的实例注入到该属性。
UsernamePasswordAuthenticationFilter的验证过程如下:
1. 首先过滤器会调用自身的attemptAuthentication方法,从request中取出authentication, authentication是在org.springframework.security.web.context.SecurityContextPersistenceFilter过滤器中通过捕获用户提交的登录表单中的内容生成的一个org.springframework.security.core.Authentication接口实例.
2. 拿到authentication对象后,过滤器会调用ProviderManager类的authenticate方法,并传入该对象
3.ProviderManager类的authenticate方法再调用自身的doAuthentication方法,在doAuthentication方法中会调用类中的List<AuthenticationProvider> providers集合中的各个AuthenticationProvider接口实现类中的authenticate(Authentication authentication)方法进行验证,由此可见,真正的验证逻辑是由各个各个AuthenticationProvider接口实现类来完成的,DaoAuthenticationProvider类是默认情况下注入的一个AuthenticationProvider接口实现类
4.AuthenticationProvider接口通过UserDetailsService来获取用户信息
需要用的类:user(用户) role(权限) userService userServiceImpl PwdAuthenticationProcessingFilter(过滤器)PwdAuthenticationToken
userServiceImpl
需要实现AuthenticationProvider接口,通过实现AuthenticationProvider接口实现类中的authenticate(Authentication authentication)方法进行验证,
boolean supports(Class<? extends Object> authentication) 方法来指定对哪个token进行操作,只有结果返回true才会执行authenticate(Authentication authentication)方法
Role要实现GrantedAuthority,GrantedAuthority接口中只有一个方法getAuthority,获取用户自定义的权限
user需要实现authentication,用来返回验证过后的用户(authentication)交给security验证权限;实现的方法,需要注意Collection<GrantedAuthority>getAuthorities(),此方法要返回实现GrantedAuthority接口的权限列表来验证用户权限。
PwdAuthenticationProcessingFilter继承AbstractAuthenticationProcessingFilter。实现attemptAuthentication方法,来进行登录的验证;还需要创建类构造方法,调用父类的构造设置访问此filter的路径(action的路径);此外还可以重写父类的afterPropertiesSet方法,来自定义处理器,处理用户登录成功或失败后跳转到的页面
涉及到的其他类:
PwdAuthenticationToken,要自己创建一个token去继承AbstractAuthenticationToken,用来存取用户信息,该类主要用来,AbstractAuthenticationProcessingFilter的实现类调用this.getAuthenticationManager().authenticate(authentication)时传递给service进行验证
-----------------------------------------------------------------------------------------------------------
执行顺序:
用户未登录时,访问/home/*的路径时,页面会自动跳转到/portal/login页面,让用户进行登录操作。
输入登录信息提交后,根据action的访问路径来找相对应的filter中的构造方法设置的访问路径,如果一致则执行该filter中的attemptAuthentication方法,在方法中根据request获取到用户的信息存储到PwdAuthenticationToken中,然后调用this.getAuthenticationManager().authenticate(authentication),authentication传入的是PwdAuthenticationToken
这时候会根据在security.xml中<security:authentication-manager alias="authenticationManager">中注入的<security:authentication-provider ref="userService">来进行判断(可以有多个authentication-provider)根据service中的supports方法来比较传入的对象PwdAuthenticationToken,如果返回值为true则执行该service中的authentication方法;在authentication方法中可以把该方法的参数转换成PwdAuthenticationToken来进行对用户名密码的校验,验证成功后获取用户的权限,注入到user中。成功后返回user验证获取权限成功,自动跳转到/home/index.jsp页面
附各类的代码
user.java
public class User implements Authentication { /** * */ private static final long serialVersionUID = 1L; private String name; private String pwd; private String loginName; @Override public String getName() { return name; } //权限 private Set<GrantedAuthority> accesses; /** * 获取权限 */ @Override public Collection<GrantedAuthority> getAuthorities() { return accesses; } @Override public Object getCredentials() { return null; } @Override public Object getDetails() { return null; } @Override public Object getPrincipal() { return this.loginName; } //判断是否验证 private boolean authenticated=false; /** * 是否已验证 */ @Override public boolean isAuthenticated() { return this.authenticated; } @Override public void setAuthenticated(boolean arg0) throws IllegalArgumentException { this.authenticated=arg0; } public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } public Set<GrantedAuthority> getAccesses() { return accesses; } public void setAccesses(Set<GrantedAuthority> accesses) { this.accesses = accesses; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } public void setName(String name) { this.name = name; } }
public class Role implements GrantedAuthority{ private static final long serialVersionUID = 1L; public static String PREFIX="ROLE_"; /** * 获取权限 */ @Override public String getAuthority() { return PREFIX+id; } /** * 初始化用户权限 *@author baozhichao 2013-12-19下午5:46:36 * @return */ public static Role getRoleUser(){ Role ro = new Role(); ro.setId("USER"); ro.setName("普通用户"); return ro; } private String id; private String name; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
public class UserServiceImpl implements UserService,AuthenticationProvider { @Override public Authentication authenticate(Authentication auth) throws AuthenticationException { PwdAuthenticationToken token = (PwdAuthenticationToken)auth; if(token.getUserName()!=null && token.getPassword()!=null){ if(!token.getUserName().equals("admin")){ token.setErrCode("1"); return null; } if(!token.getPassword().equals("123456")){ token.setErrCode("2"); return null; } User user = new User(); user.setName(token.getName()); user.setPwd(token.getPassword()); //认证成功 user.setAuthenticated(true); /**写入权限*/ Role role = Role.getRoleUser(); System.out.println(role.getAuthority()); Set<GrantedAuthority> accesses = new HashSet<GrantedAuthority>(); accesses.add(role); user.setAccesses(accesses); return user; } return null; } @Override public boolean supports(Class<? extends Object> authentication) { return authentication.equals(PwdAuthenticationToken.class); } }
public class PwdAuthenticationToken extends AbstractAuthenticationToken{ public PwdAuthenticationToken() { super(null); } private String userName; private String idCode; private String password; private String errCode; private static final long serialVersionUID = 1L; @Override public Object getCredentials() { return this.idCode+"_"+this.userName; } @Override public Object getPrincipal() { return this.password; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getIdCode() { return idCode; } public void setIdCode(String idCode) { this.idCode = idCode; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getErrCode() { return errCode; } public void setErrCode(String errCode) { this.errCode = errCode; } }
pwdAuthenticationProcessingFilter.java
public class PwdAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter { private String faildPage; /** * 必须要实现无参构造 * spring项目名称、loginCheck访问路径 */ protected PwdAuthenticationProcessingFilter() { super("/loginCheck");//设置访问路径 } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { String id = request.getParameter("id"); String name = request.getParameter("user_name"); String pwd = request.getParameter("pass_word"); PwdAuthenticationToken token = new PwdAuthenticationToken(); token.setIdCode(id); token.setUserName(name); token.setPassword(pwd); Authentication auth = null; try { auth = this.getAuthenticationManager().authenticate(token); } catch (AuthenticationException e) { e.printStackTrace(); } return auth; } @Override public void afterPropertiesSet() { super.afterPropertiesSet(); AuthenticationFaildPage faild = new AuthenticationFaildPage(); faild.setFaildPage(faildPage); this.setAuthenticationFailureHandler(faild); } public String getFaildPage() { return faildPage; } public void setFaildPage(String faildPage) { this.faildPage = faildPage; } }
import org.springframework.stereotype.Controller; /** *@author baozhichao *2013-12-19 下午3:09:17 */ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller @RequestMapping("/portal") public class LoginController { @RequestMapping(method=RequestMethod.GET,value="/login") public String login(){ return "login/login"; } }