1.基于maven下的框架,首先是对web.xml下进行配置
1.1配置servlet
1.2配置中文编码过滤器
1.3配置shiro过滤器
1.4在这里需要配置一个spring容器监听器
1.5适当情况下我们也可以配置一些错误页面跳转信息
<!--配置一个spring容器监听器 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:conf/spring-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--配置servlet -->
<servlet>
<servlet-name>SpringMvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:conf/spring-*.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>SpringMvc</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
<!--配置中文编过滤器 -->
<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>
<!--配置shiro过滤器 -->
<!--通过代理模式将spring中的bean和过滤器关联起来 -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<!--将filter的生命周期分配给servlet来掌管 -->
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<!--设置spring容器中bean的id -->
<param-name>targetBeanName</param-name>
<param-value>shiroFilter</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- <error-page>
<error-code>405</error-code>
<location>/WEB-INF/405.html</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/404.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/WEB-INF/500.jsp</location>
</error-page>
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/WEB-INF/500.jsp</location>
</error-page> -->
2.配置spring-mvc.xml
<!--开启注解扫描 -->
<context:component-scan base-package="最好是开启全局扫描"/>
<!--开启mvc 组件驱动 -->
<mvc:annotation-driven/>
<!--定义视图解析器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--开启AOP,对类进行代理 -->
<aop:config proxy-target-class="true"></aop:config>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
3.配置spring-mybatis.xml
<!--连接数据库 -->
<!--定义数据源 -->
<bean id="dataSource" class="org.apache.tomcat.dbcp.dbcp.BasicDataSource">
<property name="url" value="jdbc:mysql:///shiro?useUnicode=true&characterEncoding=UTF-8"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--配置sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:mappers/*.xml"/>
</bean>
<!--扫描指定文件下的接口文件 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<property name="basePackage" value="指定路径下的接口文件"/>
</bean>
4.配置spring-shiro.xml
<!--配置targetBean -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<!--loginURL认证提交地址,如果没有认证将会请求地址认证地址有
FormAuthenticationFilter进行认证 -->
<property name="loginUrl" value="/login.action"/>
<!--认证成功之后统一跳转到first.action -->
<property name="successUrl" value="/first.action"/>
<property name="unauthorizedUrl" value="/refuse.jsp"/>
<!--过滤链定义,从上到下将/**放到最下面 -->
<property name="filterChainDefinitions">
<value>
<!--logout.action,shiro会自动清除session -->
/logout.action = logout
/images/** = anon
/js/** = anon
/styles/** = anon
<!-- /first.action = anon -->
/** = authc
<!--匿名过滤器 -->
<!--所有的URL都可以匿名访问 -->
<!-- /** = anon -->
</value>
</property>
</bean>
<!--配置securityManager -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="customRealm"/>
</bean>
<!--配置realm -->
<bean id="customRealm" class="com.XX.shiro.CustomRealm">
<!-- 将凭证匹配器设置到realm中
凭证匹配器相当于ini文件-->
<property name="credentialsMatcher" ref="credentialsMatcher"/>
</bean>
<!-- 凭证匹配器 -->
<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="MD5"/>
<property name="hashIterations" value="1"/>
</bean>
5.首先编写一个实体类用户存放主体的身份信息和凭证信息等
public class ResultUser {
private String userId;
private String username;
private String usercode;
private List<SysPermission> menu;
private List<SysPermission> permissions;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUsercode() {
return usercode;
}
public void setUsercode(String usercode) {
this.usercode = usercode;
}
public List<SysPermission> getMenu() {
return menu;
}
public void setMenu(List<SysPermission> menu) {
this.menu = menu;
}
public List<SysPermission> getPermissions() {
return permissions;
}
public void setPermissions(List<SysPermission> permissions) {
this.permissions = permissions;
}
}
6.处理请求
@Controller
public class LoginController {
@Resource
private LoginService loginService;
@RequestMapping("/first.action")
public String first(Model model) {
Subject subject = SecurityUtils.getSubject();
ResultUser resultUser = (ResultUser) subject.getPrincipal();
model.addAttribute("resultUser", resultUser);
return "jsp/first";
}
/**
* 当前端登录界面发出login.action请求时
* @throws Exception
*/
@RequestMapping("/login.action")
public String login(String username,String password,
String randomcode,HttpSession session,
HttpServletRequest request) throws Exception {
String shiroLoginFailure = (String) request.getAttribute("shiroLoginFailure");
if (shiroLoginFailure != null) {
if (UnknownAccountException.class.getName().equals(shiroLoginFailure)) {
throw new LoginException("账户不存在");
}else if (IncorrectCredentialsException.class.getName().equals(shiroLoginFailure)) {
throw new LoginException("用户名或密码错误");
}else {
throw new Exception();//未知异常
}
}
// 此方法只做登录失败的处理,如果shiro认证登录成功以后会自动刷新跳转到上一个路径
// 登录失败
return "jsp/login";
}
}
7.自定义的realm
public class CustomRealm extends AuthorizingRealm{
@Resource
private LoginService loginService;
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// TODO Auto-generated method stub
// 从principals获取主身份信息
ResultUser resultUser =
(ResultUser) principals.getPrimaryPrincipal();
List<String> permissioncodes =
new ArrayList<String>();
List<SysPermission> permissions =
loginService.findPermissionList(resultUser.getUserId());
if (permissions != null) {
for (SysPermission permission : permissions) {
permissioncodes.add(permission.getPercode());
}
}
SimpleAuthorizationInfo authorizationInfo =
new SimpleAuthorizationInfo();
authorizationInfo.addStringPermissions(permissioncodes);
return authorizationInfo;
}
// 认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// TODO Auto-generated method stub
String usercode = (String) token.getPrincipal();
UserPojo userPojo = loginService.findUserByUsercode(usercode);
if (userPojo == null) {
return null;
}
String password = userPojo.getPassword();
// 用户信息
ResultUser resultUser = new ResultUser();
resultUser.setUserId(userPojo.getId());
resultUser.setUsercode(userPojo.getUsercode());
resultUser.setUsername(userPojo.getUsername());
// 认证时候还需要加盐
String salt = userPojo.getSalt();
List<SysPermission> menuList = loginService.findMenuList(userPojo.getId());
resultUser.setMenu(menuList);
SimpleAuthenticationInfo info =
new SimpleAuthenticationInfo(resultUser,
password,
ByteSource.Util.bytes(salt),
this.getName());
return info;
}
@Override
public void setName(String name) {
// TODO Auto-generated method stub
super.setName("customRealm");
}
通常需要对密码 进行散列,常用的有md5、sha,
对md5密码,如果知道散列后的值可以通过穷举算法,得到md5密码对应的明文。
建议对md5进行散列时加salt(盐),进行加密相当 于对原始密码+盐进行散列。
正常使用时散列方法:
在程序中对原始密码+盐进行散列,将散列值存储到数据库中,并且还要将盐也要存储在数据库中。
如果进行密码对比时,使用相同 方法,将原始密码+盐进行散列,进行比对。