一.引入相关依赖
<!--JDBC-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--spring security 引入此依赖后项目中的所有接口就会自动会保护起来(需要登录后才能访问)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mybaits-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
<version>5.1.47</version>
</dependency>
<!--druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!--spring boot 测试依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
二. 用户实体类编写
public class Hr implements UserDetails{
private Integer id;
private String name;
private String phone;
private String telephone;
private String address;
private Boolean enabled;
private String username;
private String password;
private String userface;
private String remark;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name == null ? null : name.trim();
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone == null ? null : phone.trim();
}
public String getTelephone() {
return telephone;
}
public void setTelephone(String telephone) {
this.telephone = telephone == null ? null : telephone.trim();
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address == null ? null : address.trim();
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return enabled;
}
public void setUsername(String username) {
this.username = username == null ? null : username.trim();
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> list = new ArrayList<>(roles.size());
for (Role role : roles) {
list.add(new SimpleGrantedAuthority(role.getName()));
}
return list;
}
@Override
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password == null ? null : password.trim();
}
public String getUserface() {
return userface;
}
public void setUserface(String userface) {
this.userface = userface == null ? null : userface.trim();
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark == null ? null : remark.trim();
}
}
三. 资源菜单(权限)实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Menu {
private Integer id;
private String url;
private String path;
private String component;
private String name;
private String iconCls;
private Integer parentId;
private Boolean enabled;
private Meta meta;
private List<Menu> children;
private List<Role> roles;
}
四. 角色实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Role {
private Integer id;
private String name;
private String nameZh;
}
五. 用户service实现类
@Service
public class UserServiceImpl implements UserDetailsService {
@Autowired
private HrMapper hrMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Hr hr = hrMapper.loadUserByUsername(username);
if (hr==null){
throw new UsernameNotFoundException("用户名不存在");
}
List<Role> roles = roleMapper.getRoleListByHrId(hr.getId());
hr.setRoles(roles);
return hr;
}
}
六. Security拦截过滤器
@Component
public class BeforeFilter implements FilterInvocationSecurityMetadataSource {
@Autowired
private MenuService menuService;
AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
String url = ((FilterInvocation) o).getRequestUrl();
List<Menu> menus = menuService.getAllRolesByMenus();
for (Menu menu : menus) {
if (antPathMatcher.match(menu.getUrl(), url)) {
List<Role> roles = menu.getRoles();
String[] str = new String[roles.size()];
for (int i = 0; i < roles.size(); i++) {
str[i] = roles.get(i).getName();
}
return SecurityConfig.createList(str);
}
}
return SecurityConfig.createList("ROLE_LOGIN");
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
七. 访问控制管理
@Component
public class DecisionManager implements AccessDecisionManager {
@Override
public void decide(Authentication auth, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
for (ConfigAttribute attribute : configAttributes) {
String needRole = attribute.getAttribute();
if (needRole.equals("ROLE_LOGIN")){
if (auth instanceof AnonymousAuthenticationToken){
throw new AccessDeniedException("尚未登录,请登录");
}else {
return;
}
}
Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();
for (GrantedAuthority authority : authorities) {
if (authority.getAuthority().equals(needRole)){
return;
}
}
}
throw new AccessDeniedException("权限不足,请联系管理员");
}
@Override
public boolean supports(ConfigAttribute attribute) {
return true;
}
@Override
public boolean supports(Class<?> clazz) {
return true;
}
}
八. security登录授权相关配置类
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserServiceImpl userService;
@Autowired
private BeforeFilter beforeFilter;
@Autowired
private DecisionManager decisionManager;
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().mvcMatchers("/login");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O o) {
o.setAccessDecisionManager(decisionManager);
o.setSecurityMetadataSource(beforeFilter);
return o;
}
})
.and()
.formLogin()
.loginProcessingUrl("/doLogin")
.loginPage("/login")
.usernameParameter("username")
.passwordParameter("password")
.successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp,
Authentication auth) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter writer = resp.getWriter();
Result result = new Result(ResultCode.SUCCESS);
Hr hr = (Hr) auth.getPrincipal();
hr.setPassword(null);
result.setData(hr);
result.setMessage("登录成功");
writer.write(new ObjectMapper().writeValueAsString(result));
writer.flush();
writer.close();
}
}).failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest req,
HttpServletResponse resp, AuthenticationException e) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter writer = resp.getWriter();
Result result = new Result(500, null, false);
if (e instanceof BadCredentialsException) {
result.setMessage("用户名或密码错误,请重新输入");
} else if (e instanceof LockedException) {
result.setMessage("账户被锁定,请联系管理员");
} else if (e instanceof DisabledException) {
result.setMessage("账户被禁用,请联系管理员");
} else if (e instanceof CredentialsExpiredException) {
result.setMessage("密码已过期,登录失败");
} else if (e instanceof AccountExpiredException) {
result.setMessage("账户已过期,登录失败");
}
writer.write(new ObjectMapper().writeValueAsString(result));
writer.flush();
writer.close();
}
}).permitAll()
.and()
.logout()
.logoutSuccessHandler(new LogoutSuccessHandler() {
@Override
public void onLogoutSuccess(HttpServletRequest req,
HttpServletResponse resp, Authentication auth) throws IOException,
ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter writer = resp.getWriter();
Result result = new Result(ResultCode.SUCCESS);
result.setMessage("注销成功");
writer.write(new ObjectMapper().writeValueAsString(result));
writer.flush();
writer.close();
}
}).permitAll()
.and()
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPoint() {
@Override
public void commence(HttpServletRequest req, HttpServletResponse resp,
AuthenticationException authException) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter writer = resp.getWriter();
Result result = new Result(500, null, false);
if (authException instanceof InsufficientAuthenticationException) {
result.setMessage("操作异常,请登录后访问");
}
writer.write(new ObjectMapper().writeValueAsString(result));
writer.flush();
writer.close();
}
});
}
}
九. controller测试接口
@RestController
public class LoginController {
@RequestMapping("/login")
public Result login(){
Result result = new Result(500, "尚未登录,请登录", false);
return result;
}
@RequestMapping("/hello")
public String hello(){
return "hello";
}
@RequestMapping("/employee/basic")
public String hello1(){
return "/emp/basic";
}
@RequestMapping("/employee/advanced")
public String hello2(){
return "/emp/adv";
}
}